From: Florian Müllner Date: Fri, 1 Oct 2010 01:58:59 +0000 (+0200) Subject: st-theme-node: Support non-uniform border-radii X-Git-Tag: archive/raspbian/50.5-1+rpi1~1^2~1013^2^2~9680 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=3d2d396c09b4b7cdc296338c990af8bd9a9941fb;p=gnome-shell.git st-theme-node: Support non-uniform border-radii Non-uniform border-radii are already supported when using a gradient background, this patch adds support for solid colors as well. The currently applied technique of using corner textures and filling the remaining area with rectangles is extended, so that each corner is padded with rectangles to the size of the largest corner. Add border-radius.js to test cases, to test non-uniform border-radii with both solid color and gradient backgrounds. https://bugzilla.gnome.org/show_bug.cgi?id=631091 --- diff --git a/src/st/st-theme-node-drawing.c b/src/st/st-theme-node-drawing.c index 2b8669610..718a156a1 100644 --- a/src/st/st-theme-node-drawing.c +++ b/src/st/st-theme-node-drawing.c @@ -503,6 +503,8 @@ st_theme_node_render_gradient (StThemeNode *node) void _st_theme_node_free_drawing_state (StThemeNode *node) { + int corner_id; + if (node->background_texture != COGL_INVALID_HANDLE) cogl_handle_unref (node->background_texture); if (node->background_shadow_material != COGL_INVALID_HANDLE) @@ -512,16 +514,25 @@ _st_theme_node_free_drawing_state (StThemeNode *node) if (node->border_shadow_material != COGL_INVALID_HANDLE) cogl_handle_unref (node->border_shadow_material); + for (corner_id = 0; corner_id < 4; corner_id++) + if (node->corner_texture[corner_id] != COGL_INVALID_HANDLE) + cogl_handle_unref (node->corner_texture[corner_id]); + _st_theme_node_init_drawing_state (node); } void _st_theme_node_init_drawing_state (StThemeNode *node) { + int corner_id; + node->background_texture = COGL_INVALID_HANDLE; node->background_shadow_material = COGL_INVALID_HANDLE; node->border_shadow_material = COGL_INVALID_HANDLE; node->border_texture = COGL_INVALID_HANDLE; + + for (corner_id = 0; corner_id < 4; corner_id++) + node->corner_texture[corner_id] = COGL_INVALID_HANDLE; } static void st_theme_node_paint_borders (StThemeNode *node, @@ -619,7 +630,14 @@ st_theme_node_render_resources (StThemeNode *node, } } - node->corner_texture = st_theme_node_lookup_corner (node, ST_CORNER_TOPLEFT); + node->corner_texture[ST_CORNER_TOPLEFT] = + st_theme_node_lookup_corner (node, ST_CORNER_TOPLEFT); + node->corner_texture[ST_CORNER_TOPRIGHT] = + st_theme_node_lookup_corner (node, ST_CORNER_TOPRIGHT); + node->corner_texture[ST_CORNER_BOTTOMRIGHT] = + st_theme_node_lookup_corner (node, ST_CORNER_BOTTOMRIGHT); + node->corner_texture[ST_CORNER_BOTTOMLEFT] = + st_theme_node_lookup_corner (node, ST_CORNER_BOTTOMLEFT); } static void @@ -656,8 +674,9 @@ st_theme_node_paint_borders (StThemeNode *node, { float width, height; int border_width; - int border_radius; - int max_width_radius; + int max_border_radius = 0; + int max_width_radius[4]; + int corner_id; ClutterColor border_color; CoglHandle material; @@ -665,14 +684,20 @@ st_theme_node_paint_borders (StThemeNode *node, height = box->y2 - box->y1; get_arbitrary_border (node, &border_width, &border_color); - border_radius = node->border_radius[ST_CORNER_TOPLEFT]; - - max_width_radius = MAX(border_width, border_radius); + for (corner_id = 0; corner_id < 4; corner_id++) + { + if (node->border_radius[corner_id] > max_border_radius) + max_border_radius = node->border_radius[corner_id]; + max_width_radius[corner_id] = MAX(border_width, + node->border_radius[corner_id]); + } /* borders */ if (border_width > 0) { ClutterColor effective_border; + gboolean skip_corner_1, skip_corner_2; + float x1, y1, x2, y2; over (&border_color, &node->background_color, &effective_border); @@ -681,59 +706,91 @@ st_theme_node_paint_borders (StThemeNode *node, effective_border.blue, paint_opacity * effective_border.alpha / 255); - if (border_radius > 0) /* skip corners */ - { - /* NORTH */ - cogl_rectangle (max_width_radius, 0, - width - max_width_radius, border_width); - - /* EAST */ - cogl_rectangle (width - border_width, max_width_radius, - width, height - max_width_radius); - - /* SOUTH */ - cogl_rectangle (max_width_radius, height - border_width, - width - max_width_radius, height); - - /* WEST */ - cogl_rectangle (0, max_width_radius, - border_width, height - max_width_radius); - } - else /* include corners */ - { - /* NORTH */ - cogl_rectangle (0, 0, - width, border_width); - - /* EAST */ - cogl_rectangle (width - border_width, border_width, - width, height - border_width); - - /* SOUTH */ - cogl_rectangle (0, height - border_width, - width, height); - - /* WEST */ - cogl_rectangle (0, border_width, - border_width, height - border_width); - } + /* NORTH */ + skip_corner_1 = node->border_radius[ST_CORNER_TOPLEFT] > 0; + skip_corner_2 = node->border_radius[ST_CORNER_TOPRIGHT] > 0; + + x1 = skip_corner_1 ? max_width_radius[ST_CORNER_TOPLEFT] : 0; + y1 = 0; + x2 = skip_corner_2 ? width - max_width_radius[ST_CORNER_TOPRIGHT] : width; + y2 = border_width; + cogl_rectangle (x1, y1, x2, y2); + + /* EAST */ + skip_corner_1 = node->border_radius[ST_CORNER_TOPRIGHT] > 0; + skip_corner_2 = node->border_radius[ST_CORNER_BOTTOMRIGHT] > 0; + + x1 = width - border_width; + y1 = skip_corner_1 ? max_width_radius[ST_CORNER_TOPRIGHT] : border_width; + x2 = width; + y2 = skip_corner_2 ? height - max_width_radius[ST_CORNER_BOTTOMRIGHT] + : height - border_width; + cogl_rectangle (x1, y1, x2, y2); + + /* SOUTH */ + skip_corner_1 = node->border_radius[ST_CORNER_BOTTOMLEFT] > 0; + skip_corner_2 = node->border_radius[ST_CORNER_BOTTOMRIGHT] > 0; + + x1 = skip_corner_1 ? max_width_radius[ST_CORNER_BOTTOMLEFT] : 0; + y1 = height - border_width; + x2 = skip_corner_2 ? width - max_width_radius[ST_CORNER_BOTTOMRIGHT] + : width; + y2 = height; + cogl_rectangle (x1, y1, x2, y2); + + /* WEST */ + skip_corner_1 = node->border_radius[ST_CORNER_TOPLEFT] > 0; + skip_corner_2 = node->border_radius[ST_CORNER_BOTTOMLEFT] > 0; + + x1 = 0; + y1 = skip_corner_1 ? max_width_radius[ST_CORNER_TOPLEFT] : border_width; + x2 = border_width; + y2 = skip_corner_2 ? height - max_width_radius[ST_CORNER_BOTTOMLEFT] + : height - border_width; + cogl_rectangle (x1, y1, x2, y2); } /* corners */ - if (node->corner_texture != COGL_INVALID_HANDLE) + if (max_border_radius > 0) { material = cogl_material_new (); - cogl_material_set_layer (material, 0, node->corner_texture); cogl_material_set_color4ub (material, - paint_opacity, paint_opacity, paint_opacity, paint_opacity); - + paint_opacity, paint_opacity, + paint_opacity, paint_opacity); cogl_set_source (material); - cogl_rectangle_with_texture_coords (0, 0, max_width_radius, max_width_radius, 0, 0, 0.5, 0.5); - cogl_rectangle_with_texture_coords (width - max_width_radius, 0, width, max_width_radius, 0.5, 0, 1, 0.5); - cogl_rectangle_with_texture_coords (width - max_width_radius, height - max_width_radius, width, height, 0.5, 0.5, 1, 1); - cogl_rectangle_with_texture_coords (0, height - max_width_radius, max_width_radius, height, 0, 0.5, 0.5, 1); + for (corner_id = 0; corner_id < 4; corner_id++) + { + if (node->corner_texture[corner_id] == COGL_INVALID_HANDLE) + continue; + + cogl_material_set_layer (material, + 0, node->corner_texture[corner_id]); + switch (corner_id) + { + case ST_CORNER_TOPLEFT: + cogl_rectangle_with_texture_coords (0, 0, + max_width_radius[ST_CORNER_TOPLEFT], max_width_radius[ST_CORNER_TOPLEFT], + 0, 0, 0.5, 0.5); + break; + case ST_CORNER_TOPRIGHT: + cogl_rectangle_with_texture_coords (width - max_width_radius[ST_CORNER_TOPRIGHT], 0, + width, max_width_radius[ST_CORNER_TOPRIGHT], + 0.5, 0, 1, 0.5); + break; + case ST_CORNER_BOTTOMRIGHT: + cogl_rectangle_with_texture_coords (width - max_width_radius[ST_CORNER_BOTTOMRIGHT], height - max_width_radius[ST_CORNER_BOTTOMRIGHT], + width, height, + 0.5, 0.5, 1, 1); + break; + case ST_CORNER_BOTTOMLEFT: + cogl_rectangle_with_texture_coords (0, height - max_width_radius[ST_CORNER_BOTTOMLEFT], + max_width_radius[ST_CORNER_BOTTOMLEFT], height, + 0, 0.5, 0.5, 1); + break; + } + } cogl_handle_unref (material); } @@ -743,7 +800,80 @@ st_theme_node_paint_borders (StThemeNode *node, node->background_color.blue, paint_opacity * node->background_color.alpha / 255); - if (border_radius > border_width) + /* We add padding to each corner, so that all corners end up as if they + * had a border-radius of max_border_radius, which allows us to treat + * corners as uniform further on. + */ + for (corner_id = 0; corner_id < 4; corner_id++) + { + float verts[8]; + int n_rects; + + /* corner texture does not need padding */ + if (max_border_radius == node->border_radius[corner_id]) + continue; + + n_rects = node->border_radius[corner_id] == 0 ? 1 : 2; + + switch (corner_id) + { + case ST_CORNER_TOPLEFT: + verts[0] = border_width; + verts[1] = max_width_radius[ST_CORNER_TOPLEFT]; + verts[2] = max_border_radius; + verts[3] = max_border_radius; + if (n_rects == 2) + { + verts[4] = max_width_radius[ST_CORNER_TOPLEFT]; + verts[5] = border_width; + verts[6] = max_border_radius; + verts[7] = max_width_radius[ST_CORNER_TOPLEFT]; + } + break; + case ST_CORNER_TOPRIGHT: + verts[0] = width - max_border_radius; + verts[1] = max_width_radius[ST_CORNER_TOPRIGHT]; + verts[2] = width - border_width; + verts[3] = max_border_radius; + if (n_rects == 2) + { + verts[4] = width - max_border_radius; + verts[5] = border_width; + verts[6] = width - max_width_radius[ST_CORNER_TOPRIGHT]; + verts[7] = max_width_radius[ST_CORNER_TOPRIGHT]; + } + break; + case ST_CORNER_BOTTOMRIGHT: + verts[0] = width - max_border_radius; + verts[1] = height - max_border_radius; + verts[2] = width - border_width; + verts[3] = height - max_width_radius[ST_CORNER_BOTTOMRIGHT]; + if (n_rects == 2) + { + verts[4] = width - max_border_radius; + verts[5] = height - max_width_radius[ST_CORNER_BOTTOMRIGHT]; + verts[6] = width - max_width_radius[ST_CORNER_BOTTOMRIGHT]; + verts[7] = height - border_width; + } + break; + case ST_CORNER_BOTTOMLEFT: + verts[0] = border_width; + verts[1] = height - max_border_radius; + verts[2] = max_border_radius; + verts[3] = height - max_width_radius[ST_CORNER_BOTTOMLEFT]; + if (n_rects == 2) + { + verts[4] = max_width_radius[ST_CORNER_BOTTOMLEFT]; + verts[5] = height - max_width_radius[ST_CORNER_BOTTOMLEFT]; + verts[6] = max_border_radius; + verts[7] = height - border_width; + } + break; + } + cogl_rectangles (verts, n_rects); + } + + if (max_border_radius > border_width) { /* Once we've drawn the borders and corners, if the corners are bigger * the the border width, the remaining area is shaped like @@ -756,14 +886,14 @@ st_theme_node_paint_borders (StThemeNode *node, * We draw it in 3 pieces - first the top and bottom, then the main * rectangle */ - cogl_rectangle (border_radius, border_width, - width - border_radius, border_radius); - cogl_rectangle (border_radius, height - border_radius, - width - border_radius, height - border_width); + cogl_rectangle (max_border_radius, border_width, + width - max_border_radius, max_border_radius); + cogl_rectangle (max_border_radius, height - max_border_radius, + width - max_border_radius, height - border_width); } - cogl_rectangle (border_width, max_width_radius, - width - border_width, height - max_width_radius); + cogl_rectangle (border_width, MAX(border_width, max_border_radius), + width - border_width, height - MAX(border_width, max_border_radius)); } static void @@ -1021,6 +1151,8 @@ void st_theme_node_copy_cached_paint_state (StThemeNode *node, StThemeNode *other) { + int corner_id; + g_return_if_fail (ST_IS_THEME_NODE (node)); g_return_if_fail (ST_IS_THEME_NODE (other)); @@ -1040,6 +1172,7 @@ st_theme_node_copy_cached_paint_state (StThemeNode *node, node->background_texture = cogl_handle_ref (other->background_texture); if (other->border_texture) node->border_texture = cogl_handle_ref (other->border_texture); - if (other->corner_texture) - node->corner_texture = cogl_handle_ref (other->corner_texture); + for (corner_id = 0; corner_id < 4; corner_id++) + if (other->corner_texture[corner_id]) + node->corner_texture[corner_id] = cogl_handle_ref (other->corner_texture[corner_id]); } diff --git a/src/st/st-theme-node-private.h b/src/st/st-theme-node-private.h index 48ddf4cd8..9267a6efa 100644 --- a/src/st/st-theme-node-private.h +++ b/src/st/st-theme-node-private.h @@ -78,7 +78,7 @@ struct _StThemeNode { CoglHandle border_shadow_material; CoglHandle background_texture; CoglHandle border_texture; - CoglHandle corner_texture; + CoglHandle corner_texture[4]; }; struct _StThemeNodeClass { diff --git a/tests/Makefile.am b/tests/Makefile.am index c5a77f76a..ea2164587 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -3,6 +3,7 @@ EXTRA_DIST = run-test.sh.in TEST_JS = \ interactive/borders.js \ + interactive/border-radius.js \ interactive/box-layout.js \ interactive/calendar.js \ interactive/css-fonts.js \ diff --git a/tests/interactive/border-radius.js b/tests/interactive/border-radius.js new file mode 100644 index 000000000..3d5314cd5 --- /dev/null +++ b/tests/interactive/border-radius.js @@ -0,0 +1,53 @@ +/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ + +const Clutter = imports.gi.Clutter; +const St = imports.gi.St; + +const UI = imports.testcommon.ui; + +UI.init(); +let stage = Clutter.Stage.get_default(); +stage.width = 640; +stage.height = 480; + +let vbox = new St.BoxLayout({ vertical: true, + width: stage.width, + height: stage.height, + style: 'padding: 10px;' + + 'spacing: 20px;' + + 'background: #ffee88;' }); +let scroll = new St.ScrollView(); +scroll.add_actor(vbox); +stage.add_actor(scroll); + +function addTestCase(radii, useGradient) { + let background; + if (useGradient) + background = 'background-gradient-direction: vertical;' + + 'background-gradient-start: white;' + + 'background-gradient-end: gray;'; + else + background = 'background: white;'; + + vbox.add(new St.Label({ text: "border-radius: " + radii + ";", + style: 'border: 1px solid black; ' + + 'border-radius: ' + radii + ';' + + 'padding: 5px;' + background }), + { x_fill: false }); +} + +// uniform backgrounds +addTestCase(" 0px 5px 10px 15px", false); +addTestCase(" 5px 10px 15px 0px", false); +addTestCase("10px 15px 0px 5px", false); +addTestCase("15px 0px 5px 10px", false); + +// gradient backgrounds +addTestCase(" 0px 5px 10px 15px", true); +addTestCase(" 5px 10px 15px 0px", true); +addTestCase("10px 15px 0px 5px", true); +addTestCase("15px 0px 5px 10px", true); + +stage.show(); +Clutter.main(); +stage.destroy();